<?php

/** @noinspection PhpSignatureMismatchDuringInheritanceInspection */
/**
 * MineAdmin is committed to providing solutions for quickly building web applications
 * Please view the LICENSE file that was distributed with this source code,
 * For the full copyright and license information.
 * Thank you very much for using MineAdmin.
 *
 * @Author X.Mo<root@imoi.cn>
 * @Link   https://gitee.com/xmo/MineAdmin
 */

declare(strict_types=1);

namespace Newbie\Generator;

use App\Admin\Model\Setting\SettingGenerateTables;
use App\Admin\Service\Setting\SettingGenerateColumnsService;
use Newbie\exception\NormalStatusException;
use Newbie\helper\Str;

/**
 * 模型生成
 * Class ModelGenerator
 * @package Newbie\generator
 */
class ModelGenerator extends MineGenerator implements CodeGenerator {
    /**
     * @var SettingGenerateTables
     */
    protected SettingGenerateTables $model;

    /**
     * @var string
     */
    protected string $codeContent;


    /**
     * 设置生成信息
     * @param SettingGenerateTables $model
     * @return ModelGenerator
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function setGenInfo (SettingGenerateTables $model): ModelGenerator {
        $this->model = $model;
        if (empty($model->module_name) || empty($model->menu_name)) {
            throw new NormalStatusException(t('setting.gen_code_edit'));
        }
        $this->setNamespace($this->model->namespace);
        return $this;
    }

    /**
     * 生成代码
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function generator (): void {
        $module = Str::title($this->model->module_name);
        if ($this->model->generate_type == '0') {
            $path = BASE_PATH . "/runtime/generate/{$this->model->id}/php/app/{$module}/Model/";
        } else {
            $path = BASE_PATH . "/app/{$module}/Model/";
        }
        if (!empty($this->model->package_name)) {
            $path .= Str::title($this->model->package_name) . '/';
        }
        is_dir($path) || mkdir($path, 0755, true);
        file_put_contents($path . "{$this->getClassName()}.php", $this->placeholderReplace()->getCodeContent());
    }

    /**
     * 预览代码
     */
    public function preview (): string {
        return $this->placeholderReplace()->getCodeContent();
    }

    /**
     * 获取控制器模板地址
     * @return string
     */
    protected function getTemplatePath (): string {
        return $this->getStubDir() . 'model.stub';
    }

    /**
     * 读取模板内容
     * @return string
     */
    protected function readTemplate (): string {
        return file_get_contents($this->getTemplatePath());
    }

    /**
     * 占位符替换
     */
    protected function placeholderReplace (): ModelGenerator {
        $this->setCodeContent(str_replace(
            $this->getPlaceHolderContent(),
            $this->getReplaceContent(),
            $this->readTemplate(),
        ));

        return $this;
    }

    /**
     * 获取要替换的占位符
     */
    protected function getPlaceHolderContent (): array {
        return [
            '{NAMESPACE}',
            '{CLASS_NAME}',
            '{TABLE_NAME}',
            '{FILL_ABLE}',
            '{RELATIONS}',
            '{PROPERTY}',
            '{CASTS}'
        ];
    }

    /**
     * 获取要替换占位符的内容
     */
    protected function getReplaceContent (): array {
        return [
            $this->initNamespace(),
            $this->getClassName(),
            $this->getTableName(),
            $this->getFillAble(),
            $this->getRelations(),
            $this->getProperty(),
            $this->getCasts(),
        ];
    }

    /**
     * 初始化模型命名空间
     * @return string
     */
    protected function initNamespace (): string {
        return $this->getNamespace() . "\\Model" . ($this->model->package_name ? '\\' . Str::title($this->model->package_name) : '');
    }

    /**
     * 获取类名称
     * @return string
     */
    protected function getClassName (): string {
        return $this->getBusinessName();
    }

    /**
     * 获取表名称
     * @return string
     */
    protected function getTableName (): string {
        return $this->model->table_name;
    }


    /**
     * 获取file able
     */
    protected function getFillAble (): string {
        $data    = container()->get(SettingGenerateColumnsService::class)->getList(
            ['select' => 'column_name', 'table_id' => $this->model->id]
        );
        $columns = [];
        foreach ($data as $column) {
            $columns[] = "'" . $column['column_name'] . "'";
        }
        return implode(', ', $columns);
    }

    /**
     * @return string
     */
    protected function getRelations (): string {
        $prefix = env('DB_PREFIX');
        if (!empty($this->model->options['relations'])) {
            $path    = $this->getStubDir() . 'ModelRelation/';
            $phpCode = '';
            foreach ($this->model->options['relations'] as $relation) {
                if (empty($relation['type'])) {
                    continue;
                }
                $content = file_get_contents($path . $relation['type'] . '.stub');
                $content = str_replace(
                    ['{RELATION_NAME}', '{MODEL_NAME}', '{TABLE_NAME}', '{FOREIGN_KEY}', '{LOCAL_KEY}'],
                    [$relation['name'], $relation['model'], str_replace($prefix, '', $relation['table']), $relation['foreignKey'], $relation['localKey']],
                    $content
                );
                $phpCode .= $content;
            }
            return $phpCode;
        }
        return '';
    }

    /**
     * 获取业务名称
     * @return string
     */
    public function getBusinessName (): string {
        return Str::studly(str_replace($this->dbPrefix, '', $this->model->table_name));
    }


    /**
     * 设置代码内容
     * @param string $content
     */
    public function setCodeContent (string $content): void {
        $this->codeContent = $content;
    }

    /**
     * 获取代码内容
     * @return string
     */
    public function getCodeContent (): string {
        return $this->codeContent;
    }


    protected function getProperty (): string {
        $data = container()->get(SettingGenerateColumnsService::class)->getList(
            ['select' => 'column_name,column_type,column_comment', 'table_id' => $this->model->id]
        );
        $list = [];
        foreach ($data as $val) {
            if ($val['column_type'] == 'int' || $val['column_type'] == 'bigint') {
                $type = "int";
            } elseif ($val['column_type'] == 'timestamp') {
                $type = '\\Carbon\\Carbon';
            } else {
                $type = "string";
            }
            $list[] = "* @property {$type} {$val['column_name']} {$val['column_comment']}" . \PHP_EOL;
        }
        return join(PHP_EOL, $list);
    }


    protected function getCasts (): string {
        $data = container()->get(SettingGenerateColumnsService::class)->getList(
            ['select' => 'column_name,column_type', 'table_id' => $this->model->id]
        );
        $list = [];
        foreach ($data as $val) {
            if ($val['column_type'] == 'int' || $val['column_type'] == 'bigint') {
                $list[] = "'{$val['column_name']}' => 'integer'";
            } elseif ($val['column_type'] == 'timestamp') {
                $list[] = "'{$val['column_name']}' => 'datetime'";
            }
        }
        return "[" . join(',', $list) . "]";
    }
}